#include "StdAfx.h"
#include "voter.h"
//#include "..\Lab5_EDS_RSA\RSA_System.cpp"
#include <algorithm>
#include <assert.h>
#include <time.h>
#include <stdlib.h>

using std::for_each;
using std::generate;
using std::transform;
using std::cout;
using std::cin;


voter::voter(tallier* const t, size_t questions, size_t sets)
	:_t(t), q(questions), k(sets), choozen_set(sets)
{
	// pre assertions 
	
	assert(_t != NULL);
	assert(q > 0);
	assert(k > 0);
	
	s = 1ULL << q;
	
	// preparing for sending
	
	generate_keys();
	generate_messages();
	blind_messages();



	// post assertions 

	uint128 d = _t->get_RSA_exp();
	uint128 n = _t->get_RSA_mod();

	keys* assert_result = new keys(_ckey.size());
	transform(_ckey.begin(),_ckey.end(),_fkey.begin(),assert_result->begin(), [&](const key& c, const key& f) -> uint128
	{
		return uint128::mult_m(c, f, n);
	});
	for_each(assert_result->begin(), assert_result->end(), [&](const uint128& a)
	{
		assert(a==1);
		//cout<<"m = (%I64u; %I64u)\n", a._h, a._l);
	});
	delete assert_result;
	
	messages* assert_messages = new messages(_ckey.size());
	transform(blinded_messages.begin(), blinded_messages.end(),_fkey.begin(), assert_messages->begin(), [&](const messages_set& ms_set, const key& f) -> messages_set
	{
		messages_set assert_set(ms_set.size());
		transform(ms_set.begin(), ms_set.end(), assert_set.begin(), [&](const uint128& b) -> message
		{
			return uint128::mult_m(uint128::pow_m(f, d, n), b, n);
		});
		return assert_set;
	});
	assert(*assert_messages==_messages);
	delete assert_messages;
}

void voter::generate_keys()
{	
	_ckey.resize(k);
	_fkey.resize(k);

	uint128 n = _t->get_RSA_mod();

	generate(_ckey.begin(), _ckey.end(), [&]() -> uint128
	{
		uint128 c;
		do
		{
			c = uint128::random() % n;
		}
		while((uint128::gcd(c, n - 1) != 1) || (uint128::gcd(c, n) != 1));

		return c;
	});



	size_t ckey_size = _ckey.size();
	transform(_ckey.begin(), _ckey.end(), _fkey.begin(), [&](const uint128& c) -> uint128
	{
		return c.inv(n);
	});


	assert(_ckey.size() == _fkey.size());

	cout<<"C keys:\n";
	for_each(_ckey.begin(), _ckey.end(), [&](uint128 c)
	{
		cout<<"c = "<<c<<"\t";
	});
	cout<<"\n\n";

	cout<<"F keys:\n";
	for_each(_fkey.begin(), _fkey.end(), [&](uint128 f)
	{
		cout<<"f = "<<f<<"\t";
	});
	cout<<"\n\n";
}

void voter::generate_messages()
{
	_messages.resize(k);
	uint128 n = _t->get_RSA_mod();
	generate(_messages.begin(), _messages.end(), [&]() -> messages_set
	{
		messages_set b(s);
		for(unsigned int i = 0; i < s; ++i)
		{
			b[i] = i + SALT;
		}
		
		return b;
	});

	cout<<"Messages M:\n";
	for_each(_messages.begin(), _messages.end(), [&](messages_set ms)
	{
		for_each(ms.begin(), ms.end(), [&](uint128 m)
		{
			cout<<"m = "<<m - SALT<<"\t";
		});
		cout<<"\n";
	});
	cout<<"\n\n";
}

void voter::blind_messages()
{
	assert(_messages.size() == _ckey.size());
	assert(_messages.size() == k);

	blinded_messages.resize(k);
	uint128 d = _t->get_RSA_exp();
	uint128 n = _t->get_RSA_mod();

	transform(_messages.begin(), _messages.end(),_ckey.begin(), blinded_messages.begin(), [&](const messages_set& ms_set, const key& c) -> messages_set
	{
		messages_set blinded_set(ms_set.size());
		transform(ms_set.begin(), ms_set.end(), blinded_set.begin(), [&](const uint128& m) -> message
		{
			return uint128::mult_m(uint128::pow_m(c, d, n), m, n);
		});
		return blinded_set;
	});

	
}

const messages& voter::get_blinded_messages() const
{
	assert(blinded_messages.size()==k);
	cout<<"Blinded Messages:\n";
	for_each(blinded_messages.begin(), blinded_messages.end(), [&](messages_set ms)
	{
		for_each(ms.begin(), ms.end(), [&](uint128 m)
		{
			cout<<"m = "<<m<<"\t";
		});
		cout<<"\n";
	});
	cout<<"\n\n";
	return blinded_messages;
}

const keys voter::get_unblinded_keys(const size_t signed_index) const
{
	assert((signed_index < k) && (signed_index >= 0)); 

	((voter*)this)->choozen_set = signed_index; // dirty hack against const, but as usual

	keys unblinded = _fkey;
	unblinded.erase(unblinded.begin() + signed_index);

	cout<<"F keys:\n";
	for_each(unblinded.begin(), unblinded.end(), [&](uint128 f)
	{
		cout<<"f = "<<f<<"\t";
	});
	cout<<"\n";

	return unblinded;
}
const messages_set voter::get_signed_messages_set(const messages_set& signed_blinded_messages_set)
{
	assert(signed_blinded_messages_set.size() == s);
	assert((choozen_set < k) && (choozen_set >= 0));

	signed_messages.resize(signed_blinded_messages_set.size());
	uint128 n = _t->get_RSA_mod();
	uint128 f = _fkey[choozen_set];

	transform(signed_blinded_messages_set.begin(), signed_blinded_messages_set.end(),signed_messages.begin(), [&](const message& q) -> message
	{
		return uint128::mult_m(f, q, n);
	});

	cout<<"Signed messages S:\n";
	for_each(signed_messages.begin(), signed_messages.end(), [&](uint128 m)
	{
		cout<<"m = "<<m<<"\t";
	});
	cout<<"\n";

	return signed_messages;
}
const signed_message voter::make_choice()
{
	size_t choice;
	assert(signed_messages.size() == s);
	
	cout<<"Enter the vote [0.."<< s - 1 <<"] : ";
	cin>>choice;

	assert( (choice >= 0) && (choice < s));

	//srand(unsigned(time(NULL)));
	//size_t choice = rand() % q; // the choice is here

	signed_message result;
	result.message = _messages[choozen_set][choice];
	result.signature = signed_messages[choice];

	cout<<"Choosen message number "<<choice<<"\n";
	cout<<"message is: "<< result.message - SALT<<"\t";
	cout<<"signature is: "<< result.signature<<"\t";

	cout<<"\n";
	return result;
}
voter::~voter()
{
	//if(_ckey != NULL) delete _ckey;
	//if(_fkey != NULL) delete _fkey;
	//if(_messages != NULL) delete _messages;
}